﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web;
using System.Reflection;
using System.IO;

using Job.Core.Module;

namespace Job.Core.Web
{
    /// <summary>
    /// 对 HttpContext 的扩展。
    /// </summary>
    public static class HttpContextExtensions
    {
        /// <summary>
        /// 指示当前是否是一个 Json 请求。
        /// </summary>
        /// <param name="context"></param>
        /// <returns>是返回 true，否则返回 false。</returns>
        public static bool IsJsonRequest(this HttpContext context)
        {
            return context.Request.Headers["Accept"] != null && context.Request.Headers["Accept"].Contains("application/json");
        }

        /// <summary>
        /// 指示当前是否为一个 Post 提交。
        /// </summary>
        /// <param name="context"></param>
        /// <returns>是返回 true，否则返回 false。</returns>
        public static bool IsPostSubmit(this HttpContext context)
        {
            return context.Request.RequestType == "POST";
        }

        /// <summary>
        /// 向客户端输出 Json 数据并终止输出。
        /// </summary>
        /// <param name="context"></param>
        /// <param name="obj">要输出的对象。</param>
        public static void WriteJson(this HttpContext context, object obj)
        {
            if (obj == null)
                throw new ArgumentNullException("obj");

            context.Response.ContentType = "application/json";
            context.Response.ContentEncoding = System.Text.Encoding.UTF8;
            context.Response.Write(obj.ToJson());
            context.Response.End();
        }

        /// <summary>
        /// 向客户端输出 html 数据并终止输出。
        /// </summary>
        /// <param name="context"></param>
        /// <param name="code">要输出的code。</param>
        public static void WriteHtml(this HttpContext context, string code)
        {
            if (string.IsNullOrEmpty(code))
                code = string.Empty;

            context.Response.ContentType = "txt/html";
            context.Response.ContentEncoding = System.Text.Encoding.UTF8;
            context.Response.Write(code);
            context.Response.End();
        }


        /// <summary>
        /// 在当前 HttpContext 中呈现指定控件。
        /// </summary>
        /// <param name="context"></param>
        /// <param name="virtualPath">控件虚拟路径。</param>
        public static void RenderControl(this HttpContext context, string virtualPath)
        {
            Page page = new Page();
            Control control = page.LoadControl(virtualPath);

            using (StringWriter sw = new StringWriter())
            {
                page.Controls.Add(control);
                context.Server.Execute(page, sw, true);
                context.Response.Write(sw.ToString());
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TModel"></typeparam>
        /// <param name="context"></param>
        /// <param name="entity"></param>
        /// <param name="includeProperties"></param>
        /// <param name="excludeProperties"></param>
        public static void UpdateModel<TKey, TModel>(this HttpContext context, TModel entity, string[] includeProperties, string[] excludeProperties)
            where TModel : Business<TKey, TModel>, new()
            where TKey : IComparable
        {
            PropertyInfo[] properties = entity.GetPropertyInfo();
            foreach (PropertyInfo property in properties)
            {
                string key = property.Name;
                if (!property.CanWrite)
                    continue;

                if (includeProperties != null)
                {
                    string val = includeProperties.FirstOrDefault(s => string.Compare(s, key, true) == 0);
                    if (val == null)
                        continue;
                }

                if (excludeProperties != null)
                {
                    string val = excludeProperties.FirstOrDefault(s => string.Compare(s, key, true) == 0);
                    if (val != null)
                        continue;
                }

                Type pt = property.PropertyType;

                if (context.Request.Form[key] == null && pt != typeof(Boolean) && pt != typeof(Boolean?))
                    continue;

                try
                {

                    if (pt == typeof(String))
                        property.SetValue(entity, context.Request.Form[key], null);
                    else if (pt == typeof(Int32))
                        property.SetValue(entity, Int32.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Int32?))
                    {
                        string form = context.Request.Form[key];
                        Int32 val;
                        if (Int32.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(Boolean))
                        property.SetValue(entity, !string.IsNullOrEmpty(context.Request.Form[key]), null);
                    else if (pt == typeof(Boolean?))
                    {
                        string form = context.Request.Form[key];
                        if (!string.IsNullOrEmpty(form))
                            property.SetValue(entity, true, null);
                    }
                    else if (pt == typeof(Decimal))
                        property.SetValue(entity, Decimal.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Decimal?))
                    {
                        string form = context.Request.Form[key];
                        Decimal val;
                        if (Decimal.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(DateTime))
                        property.SetValue(entity, DateTime.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(DateTime?))
                    {
                        string form = context.Request.Form[key];
                        DateTime val;
                        if (DateTime.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt.IsEnum)
                        property.SetValue(entity, Enum.Parse(pt, context.Request.Form[key]), null);
                    else if (pt == typeof(Int64))
                        property.SetValue(entity, Int64.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Int64?))
                    {
                        string form = context.Request.Form[key];
                        Int64 val;
                        if (Int64.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(Single))
                        property.SetValue(entity, Single.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Single?))
                    {
                        string form = context.Request.Form[key];
                        Single val;
                        if (Single.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(Double))
                        property.SetValue(entity, Double.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Double?))
                    {
                        string form = context.Request.Form[key];
                        Double val;
                        if (Double.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(Int16))
                        property.SetValue(entity, Int16.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Int16?))
                    {
                        string form = context.Request.Form[key];
                        Int16 val;
                        if (Int16.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(Byte))
                        property.SetValue(entity, Byte.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Byte?))
                    {
                        string form = context.Request.Form[key];
                        Byte val;
                        if (Byte.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(Guid))
                        property.SetValue(entity, new Guid(context.Request.Form[key]), null);
                }
                catch
                {
                    throw new ArgumentException(key);
                }
            }
        }

        /// <summary>
        /// 从 Request.Form 请求中填充一个对象。
        /// </summary>
        /// <typeparam name="T">类型。</typeparam>
        /// <param name="control"></param>
        /// <param name="entity">要填充的对象。</param>
        /// <param name="includeProperties">要填充的属性。</param>
        /// <param name="excludeProperties">要排除的属性。</param>
        public static void UpdateModel<T>(this HttpContext context, T entity, string[] includeProperties, string[] excludeProperties)
            where T : class
        {

            if (entity == null)
                throw new ArgumentNullException("entity");

            Type type = typeof(T);
            PropertyInfo[] properties = type.GetProperties();

            foreach (PropertyInfo property in properties)
            {
                string key = property.Name;
                if (!property.CanWrite)
                    continue;

                if (includeProperties != null)
                {
                    string val = includeProperties.FirstOrDefault(s => string.Compare(s, key, true) == 0);
                    if (val == null)
                        continue;
                }

                if (excludeProperties != null)
                {
                    string val = excludeProperties.FirstOrDefault(s => string.Compare(s, key, true) == 0);
                    if (val != null)
                        continue;
                }
                
                Type pt = property.PropertyType;               

                if (context.Request.Form[key] == null && pt != typeof(Boolean) && pt != typeof(Boolean?))
                    continue;

                try
                {
                    
                    if (pt == typeof(String))
                        property.SetValue(entity, context.Request.Form[key], null);
                    else if (pt == typeof(Int32))
                        property.SetValue(entity, Int32.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Int32?))
                    {
                        string form = context.Request.Form[key];
                        Int32 val;
                        if (Int32.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(Boolean))
                        property.SetValue(entity, !string.IsNullOrEmpty(context.Request.Form[key]), null);
                    else if (pt == typeof(Boolean?))
                    {
                        string form = context.Request.Form[key];
                        if (!string.IsNullOrEmpty(form))
                            property.SetValue(entity, true, null);
                    }
                    else if (pt == typeof(Decimal))
                        property.SetValue(entity, Decimal.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Decimal?))
                    {
                        string form = context.Request.Form[key];
                        Decimal val;
                        if (Decimal.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(DateTime))
                        property.SetValue(entity, DateTime.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(DateTime?))
                    {
                        string form = context.Request.Form[key];
                        DateTime val;
                        if (DateTime.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt.IsEnum)
                        property.SetValue(entity, Enum.Parse(pt, context.Request.Form[key]), null);
                    else if (pt == typeof(Int64))
                        property.SetValue(entity, Int64.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Int64?))
                    {
                        string form = context.Request.Form[key];
                        Int64 val;
                        if (Int64.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(Single))
                        property.SetValue(entity, Single.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Single?))
                    {
                        string form = context.Request.Form[key];
                        Single val;
                        if (Single.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(Double))
                        property.SetValue(entity, Double.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Double?))
                    {
                        string form = context.Request.Form[key];
                        Double val;
                        if (Double.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(Int16))
                        property.SetValue(entity, Int16.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Int16?))
                    {
                        string form = context.Request.Form[key];
                        Int16 val;
                        if (Int16.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(Byte))
                        property.SetValue(entity, Byte.Parse(context.Request.Form[key]), null);
                    else if (pt == typeof(Byte?))
                    {
                        string form = context.Request.Form[key];
                        Byte val;
                        if (Byte.TryParse(form, out val))
                            property.SetValue(entity, val, null);
                    }
                    else if (pt == typeof(Guid))
                        property.SetValue(entity, new Guid(context.Request.Form[key]), null);
                }
                catch
                {
                    throw new ArgumentException(key);
                }
            }
        }
    }
}
